package com.atos.optical.utils;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.sql.Timestamp;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author wanghra
 * @version 1.0
 * @description 缓存工具类
 * @date 2025/8/11
 */
@Slf4j
public class CacheUtil {
    /**
     * 缓存大小
     */
    private static int CACHE_SIZE = 128;
    /**
     * 默认存放时间：10min
     */
    private static final long DEFAULT_EXPIRE_TIME = 1000 * 60 * 10L;
    /**
     * 永久生效时间
     */
    private static final long FOREVER_NO_EXPIRE_TIME = -1L;
    /**
     * 存放缓存对象Map，默认保存128个
     */
    private static final ConcurrentHashMap<String, CacheObject> CACHE_MAP = new ConcurrentHashMap<>(CACHE_SIZE);
    /**
     * 缓存清理线程
     */
    private static final CacheCleanThread CACHE_CLEAN_THREAD = new CacheCleanThread(DEFAULT_EXPIRE_TIME, true);

    static {
        /**此处可以结合线程池工具类*/
        new Thread(CACHE_CLEAN_THREAD).start();
    }

    /**
     * 默认存放方法
     *
     * @param key
     * @param value
     */
    public static void put(String key, Object value) {
        if (null == value) {
            log.error("NULL is not save in CacheUtil");
            return;
        }

        if (isMax()) {
            cleanExpireCache();
            if (isMax()) {
                log.error("Cache is full, can not add");
                return;
            }
        }

        CACHE_MAP.put(key, new CacheObject(value, DEFAULT_EXPIRE_TIME));
    }

    /**
     * 指定过期时间存放方法
     *
     * @param key
     * @param value
     * @param expireTime
     */
    public static void put(String key, Object value, long expireTime) {
        if (null == value) {
            log.error("NULL is not save in CacheUtil");
            return;
        }

        if (isMax()) {
            cleanExpireCache();
            if (isMax()) {
                log.error("Cache is full, can not add");
                return;
            }
        }

        if (FOREVER_NO_EXPIRE_TIME != expireTime && 0 >= expireTime) {
            expireTime = DEFAULT_EXPIRE_TIME;
        }

        CACHE_MAP.put(key, new CacheObject(value, expireTime));
    }

    /**
     * 取缓存对象
     *
     * @param key
     * @return
     */
    public static Object get(String key) {
        if (isExist(key)) {
            return CACHE_MAP.get(key).value;
        }
        return null;
    }

    /**
     * 根据类型指定返回类型
     *
     * @param key
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T get(String key, Class<T> clazz) {
        if (isExist(key)) {
            return clazz.cast(CACHE_MAP.get(key).value);
        }
        return null;
    }

    /**
     * 是否存在key值value
     *
     * @param key
     * @return
     */
    public static boolean isExist(String key) {
        CacheObject value = CACHE_MAP.get(key);
        if (null == value) {
            log.error("NULL is value, please check cache");
            return false;
        }

        return !isExpire(value);
    }

    /**
     * 缓存是否最大
     *
     * @return
     */
    public static boolean isMax() {
        return CACHE_MAP.size() >= CACHE_SIZE;
    }

    /**
     * 当前缓存大小
     *
     * @return
     */
    public static int nowCacheSize() {
        return CACHE_MAP.size();
    }

    /***************************************************Cache内部方法***************************************************************/
    /**
     * 缓存对象是否过期
     *
     * @param value
     * @return
     */
    private static boolean isExpire(CacheObject value) {
        if (null == value) {
            log.error("NULL is value, return true and wait clean");
            return true;
        }

        if (FOREVER_NO_EXPIRE_TIME == value.expireTime) {
            return false;
        }

        return new Timestamp(System.currentTimeMillis()).after(new Timestamp(value.timeStamp + value.expireTime));
    }

    /**
     * 清理过期缓存
     */
    private static void cleanExpireCache() {
        for (String key : CACHE_MAP.keySet()) {
            CacheObject value = CACHE_MAP.get(key);
            if (isExpire(value)) {
                CACHE_MAP.remove(key);
                log.info("Clean cache key:【{}】", key);
            }
        }
    }

    /***************************************************debug调试方法***************************************************************/
    /**
     * 获取所有缓存信息
     *
     * @return
     */
    public static JSONObject getAllCache() {
        JSONObject result = new JSONObject();
        JSONArray expireArray = new JSONArray();
        JSONArray cacheArray = new JSONArray();
        CACHE_MAP.keySet().forEach(key -> {
            CacheObject value = CACHE_MAP.get(key);
            JSONObject jsonObject = new JSONObject();
            jsonObject.put(key, value.value);
            if (isExpire(value)) {
                expireArray.add(jsonObject);
            } else {
                jsonObject.put("time", value.expireTime == FOREVER_NO_EXPIRE_TIME ? -1 : value.timeStamp + value.expireTime - System.currentTimeMillis());
                cacheArray.add(jsonObject);
            }
        });
        result.put("expire", expireArray);
        result.put("cache", cacheArray);
        return result;
    }

    /**
     * 动态扩展缓存大小
     *
     * @param cacheSize
     */
    public static void extendCacheSize(int cacheSize) {
        if (CACHE_SIZE >= cacheSize) {
            log.info("CacheSize less than now size【{}】, cancel extend cache", CACHE_SIZE);
            return;
        }
        CACHE_SIZE = cacheSize;
    }

    /**
     * 控制清理线程是否执行清理任务
     *
     * @param isStartClean
     */
    public static void setIsStartClean(boolean isStartClean) {
        CACHE_CLEAN_THREAD.setStartClean(isStartClean);
    }

    /**
     * 控制清理线程刷新时间
     *
     * @param flushTime
     */
    public static void setCleanTime(long flushTime) {
        CACHE_CLEAN_THREAD.setFlushTime(flushTime);
    }

    /**
     * 手动触发缓存清理
     */
    public static void triggerCleanCache() {
        log.info("Start to clean expire cache");
        cleanExpireCache();
        log.info("Clean expire cache end");
    }

    /***************************************************Cache对象和清理线程***********************************************************/
    @Data
    private static class CacheObject {
        /**
         * 值
         */
        private Object value;
        /**
         * 过期时间（毫秒）
         */
        private long expireTime;
        /**
         * 创建时间戳
         */
        private long timeStamp;

        public CacheObject(Object value, long expireTime) {
            this.value = value;
            this.expireTime = expireTime;
            this.timeStamp = System.currentTimeMillis();
        }
    }

    /**
     * 定时清理缓存类线程
     */
    @Data
    @AllArgsConstructor
    private static class CacheCleanThread implements Runnable {
        /**
         * 刷新频率
         */
        private long flushTime;
        /**
         * 线程执行开关
         */
        private boolean isStartClean;

        @Override
        public void run() {
            while (true) {
                try {
                    if (isStartClean) {
                        log.info("Start to clean expire cache");
                        cleanExpireCache();
                        log.info("Clean expire cache end, now flushTime:【{}】", flushTime);
                    }
                    Thread.sleep(flushTime);
                } catch (Exception e) {
                    log.error("CacheCleanThread error：{}", e.getMessage());
                }
            }
        }
    }
}
